package main

import (
	"encoding/json"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	echojwt "github.com/labstack/echo-jwt/v4"
	"github.com/labstack/echo/v4"
	"log"
	"net/http"
	"strings"
	"time"
)

var jwtKey = []byte("secret")

const (
	contextKeyUser = "uInfo"
)

// JWT 提供了一个 JSON Web Token (JWT) 认证中间件。
//
// 对于有效的 token，它将用户置于上下文中并调用下一个处理程序。
// 对于无效的 token，它会发送 "401 - Unauthorized" 响应。
// 对于丢失或无效的 Authorization 标头，它会发送 "400 - Bad Request" 。
func main() {

	e := echo.New()

	e.GET("/login", Login)

	//方式一：直接使用，会自动从请求头中获取token TokenLookup: "header:Authorization:Bearer "
	userGroup := e.Group("/default", echojwt.JWT(jwtKey), GetUserFormTokenMiddleware)
	userGroup.GET("/get", GetUserFromContext)

	//方式二： 自定义config,会自动从请求头中获取token TokenLookup： "header:Auth:RZ "
	carGroup := e.Group("/custom")
	carGroup.Use(echojwt.WithConfig(CustomConfig()), GetUserFormTokenMiddleware)
	carGroup.GET("/get", GetUserFromContext)

	e.Start(":80")
}

// Login
//
//	@Description: 登录
//	@param c
//	@return error
func Login(c echo.Context) error {
	username := c.QueryParam("username")
	password := c.QueryParam("password")
	// 这里应该有用户认证逻辑，这里仅作为示例
	if username == "admin" && password == "123456" {
		user := User{
			UserId:   1,
			UserName: "admin",
			Password: "123456",
			Email:    "admin@qq.com",
		}
		//json格式化
		userInfo, _ := json.Marshal(user)
		//生成token
		token := GenJwtToken(string(userInfo))
		return c.String(http.StatusOK, token)
	} else {
		return echo.NewHTTPError(http.StatusForbidden, "用户名或密码错误")
	}
}

// GenJwtToken
//
//	@Description: 生成token
//	@return string
func GenJwtToken(subject string) string {
	claims := jwt.StandardClaims{
		ExpiresAt: time.Now().Add(time.Hour * 24).Unix(),
		Subject:   subject,
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tokenStr, _ := token.SignedString(jwtKey)
	return tokenStr
}

// GetUserInfoFromToken
//
//	@Description: 从token中获取user信息
//	@param tokenStr
//	@return *User
//	@return error
func GetUserInfoFromToken(tokenStr string) (*User, error) {
	claims, err := parseJwtToken(tokenStr)
	if err != nil {
		return nil, err
	}
	userInfo := &User{}
	err = json.Unmarshal([]byte(claims.Subject), userInfo)
	if err != nil {
		return nil, err
	}
	return userInfo, nil
}

// 解析和验证 JWT Token 的函数
func parseJwtToken(tokenString string) (*jwt.StandardClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) {
		// 验证签名算法
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
		}
		return jwtKey, nil
	})
	if err != nil {
		return nil, err
	}
	// 验证 token 是否有效
	if claims, ok := token.Claims.(*jwt.StandardClaims); ok && token.Valid {
		return claims, nil
	}
	return nil, fmt.Errorf("invalid token")
}

// CustomConfig
//
//	@Description: 自定义配置
//	@return echojwt.Config
func CustomConfig() echojwt.Config {
	c := echojwt.Config{
		Skipper: func(c echo.Context) bool {
			uri := c.Request().RequestURI
			if strings.Contains(uri, "/login") {
				log.Println("uri:", uri, "--->>>Skipper-true")
				return true
			}
			log.Println("uri:", uri, "--->>>Skipper-false")
			return false
		},
		SigningKey: jwtKey,
		//TokenLookup: "query:token", //表示在请求url上  http://localhost:80/custom/get?token=xxxxx
		TokenLookup: "header:Auth:RZ ", //表示在请求头上  Auth: RZ xxxxxxxtokenxxxxxx
	}
	return c
}

// GetUserFormTokenMiddleware
//
//	@Description: 从header中获取token,然后解析出user,再将user放入context中
//	@param next
//	@return echo.HandlerFunc
func GetUserFormTokenMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		var token = ""
		//"header:Auth:RZ "
		auth := c.Request().Header.Get("Auth")
		if auth != "" {
			split := strings.Split(auth, " ")
			token = split[1]
		}
		// "header:Authorization:Bearer "
		authorization := c.Request().Header.Get("Authorization")
		if authorization != "" {
			split := strings.Split(auth, " ")
			token = split[1]
		}
		if token == "" {
			return echo.NewHTTPError(http.StatusForbidden, "token为空")
		}

		//从token中解析出user信息
		user, err := GetUserInfoFromToken(token)
		if err != nil {
			return echo.NewHTTPError(http.StatusForbidden, "token无效")
		}
		//log.Println("user:", *user)

		//将user放入context中
		c.Set(contextKeyUser, user)

		// 调用下一个中间件或处理函数
		err = next(c)

		// 在请求处理之后执行的逻辑
		if err != nil {
			c.Error(err)
		}
		log.Println("CheckTokenExist-post")
		return nil
	}
}

func GetUserFromContext(c echo.Context) error {
	user := c.Get(contextKeyUser)
	return c.JSON(http.StatusOK, user)
}

type User struct {
	UserId   int    `json:"userid"`
	UserName string `json:"username"`
	Password string `json:"password"`
	Email    string `json:"email"`
}
